package fun.ipconfig.shopping.configure;

import fun.ipconfig.shopping.configure.context.DefaultSecurityContext;
import fun.ipconfig.shopping.configure.filter.AuthenticationFilter;
import fun.ipconfig.shopping.dao.UserDetailsDao;
import fun.ipconfig.shopping.entity.UserEntity;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.annotation.web.configurers.FormLoginConfigurer;
import org.springframework.security.config.annotation.web.configurers.SecurityContextConfigurer;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.logout.LogoutFilter;
import org.springframework.security.web.context.DelegatingSecurityContextRepository;
import org.springframework.security.web.context.HttpSessionSecurityContextRepository;
import org.springframework.security.web.context.RequestAttributeSecurityContextRepository;
import org.springframework.security.web.context.SecurityContextRepository;

/**
 * @author gtl
 */
@Configuration
public class SecurityConfig {
    
    private final UserDetailsDao userDetailsDao;

    public SecurityConfig(UserDetailsDao userDetailsDao) {
        this.userDetailsDao = userDetailsDao;
    }

    /**
     * 实现用户查询的接口，获取用户信息
     * @return
     */
    @Bean
    public UserDetailsService userDetailsService() {
        return username -> {
            UserEntity userEntity = userDetailsDao.getUser(username);
            if(userEntity == null){
                throw new UsernameNotFoundException("账号不存在");
            }
            return userDetailsDao.getUser(username);
        };
    }

    /**
     * 加埋方式
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 过滤器配置
     * @param http
     * @param securityContextRepository
     * @param authenticationFilter
     * @return
     * @throws Exception
     */
    @Bean
    public SecurityFilterChain securityFilterChain(
            HttpSecurity http,
            SecurityContextRepository securityContextRepository,
            AuthenticationFilter authenticationFilter) throws Exception {
        http.authorizeHttpRequests(authorize -> authorize
                .requestMatchers(
                        "/success",
                        "/fail/*",
                        "/login")
                .permitAll()
                .anyRequest()
                .authenticated()
        ).securityContext(
                /**
                 * 登录状态管理
                 */
                context -> context.securityContextRepository(
                        securityContextRepository
                )
        ).addFilterAfter(
                /**
                 * 自定义用户登录过滤器
                 */
                authenticationFilter,
                LogoutFilter.class
        ).csrf(AbstractHttpConfigurer::disable);
        
        /**
         * 胜出默认的UsernamePasswordAuthenticationFilter
         */
        http.removeConfigurer(FormLoginConfigurer.class);
        return http.build();
    }

    /**
     * 会话上下文管理
     * @return
     */
    @Bean
    public SecurityContextRepository securityContextRepository(){
        return new DelegatingSecurityContextRepository(
                new HttpSessionSecurityContextRepository(),
                new DefaultSecurityContext()
        );
    }

    /**
     * 认证管理
     * @param userDetailsService
     * @param passwordEncoder
     * @return
     */
    @Bean
    public AuthenticationManager authenticationManager(UserDetailsService userDetailsService, PasswordEncoder passwordEncoder) {
        DaoAuthenticationProvider authenticationProvider = new DaoAuthenticationProvider();
        authenticationProvider.setUserDetailsService(userDetailsService);
        authenticationProvider.setPasswordEncoder(passwordEncoder);
        return new ProviderManager(authenticationProvider);
    }

}
